import AStar from "./AStar/AStar";
import AStarCheckTag from "./AStar/AStarCheckTag";
import { AStarNode } from "./AStar/AStarNode";
import JPS from "./JPS/JPS";
import JPSCheckTag from "./JPS/JPSCheckTag";
import JPSNode from "./JPS/JPSNode";
import PointNode from "./PointNode";
import TileData from "./TileData";

const { ccclass, property } = cc._decorator;

@ccclass
export default class Helloworld extends cc.Component {
  @property(cc.Prefab)
  pointPrefab: cc.Prefab = null;

  graphics: cc.Graphics = null;
  tileLayerInfo: cc.TiledLayer = null;
  tileMapInfo: cc.TiledMap = null;
  tileSize: cc.Size = null;
  mapSize: cc.Size = null;
  tileMapNode: cc.Node = null;
  tileNodeList: Array<PointNode>;

  astarNodes: Array<AStarNode<any, number>>;
  astarLogic: AStar<AStarNode<any, number>, any, number>;
  startNode: AStarNode<any, number>;
  endNode: AStarNode<any, number>;
  finalPath: Array<AStarNode<any, number>>;

  jpsNodes: Array<JPSNode<any, number>>;
  jpsLogic: JPS<JPSNode<any, number>, any, number>;
  jpsStartNode: JPSNode<any, number>;
  jpsEndNode: JPSNode<any, number>;
  jpsFindPath: Array<JPSNode<any, number>>;



  start() {
    this.debugAvgTime = 0;
    this.debugCount = 0;
    this.debugTotalTime = 0;
    this.jps();
    // this.astat();
  }

  debugCount: number;
  debugAvgTime: number;
  debugTotalTime: number;
  private astat() {
    this.tileMapNode = this.node.getChildByName("tiledmap");
    this.graphics = this.tileMapNode.getComponent(cc.Graphics);
    this.tileLayerInfo = this.tileMapNode.getComponent(cc.TiledMap).getLayers()[0];
    this.tileMapInfo = this.tileMapNode.getComponent(cc.TiledMap);
    this.tileSize = this.tileMapInfo.getTileSize();
    this.mapSize = this.tileMapInfo.getMapSize();
    this.tileNodeList = new Array();
    this.astarNodes = new Array();
    this.initAstarTileList();
    this.astarLogic = new AStar();
    this.astarLogic.setOnSearchCall((node: AStarNode<any, number>) => {
      let pnode = this.tileNodeList[node.myIndex];
      pnode.setF(node.f)
      pnode.setG(node.g)
      pnode.setH(node.h)
      pnode.setDir(node.visitCount);
      pnode.lightOn();
    })

    let startTime = new Date().getTime();
    for (let i = 0; i < 1; i++) {
      this.finalPath = this.astarLogic.findPath(this.startNode, this.endNode, this.astarNodes, new AStarCheckTag(0), new AStarCheckTag(1), this.mapSize.width, this.mapSize.height);
    }
    let endTime = new Date().getTime();
    cc.log(endTime - startTime);
    if (this.finalPath) {
      this.graphics.clear();
      this.lightIndex = this.finalPath.length - 1;
      this.interval = setInterval(this.lightOnAstarNode.bind(this), 100);
    }
  }

  private jps() {
    this.tileMapNode = this.node.getChildByName("tiledmap");
    this.graphics = this.tileMapNode.getComponent(cc.Graphics);
    this.tileLayerInfo = this.tileMapNode.getComponent(cc.TiledMap).getLayers()[0];
    this.tileMapInfo = this.tileMapNode.getComponent(cc.TiledMap);
    this.tileSize = this.tileMapInfo.getTileSize();
    this.mapSize = this.tileMapInfo.getMapSize();
    this.tileNodeList = new Array();
    this.jpsNodes = new Array();
    this.initJPSTileList();
    this.jpsLogic = new JPS();
    this.jpsLogic.setOnSearchCall((node: JPSNode<any, number>) => {
      let pnode = this.tileNodeList[node.myIndex];
      pnode.setF(node.f)
      pnode.setG(node.g)
      pnode.setH(node.h)
      pnode.setDir(node.visitCount);
      pnode.lightOn();
    })

    let startTime = new Date().getTime();
    for (let i = 0; i < 1; i++) {
      this.jpsFindPath = this.jpsLogic.findPath(this.jpsStartNode, this.jpsEndNode, this.jpsNodes, new JPSCheckTag(0), new JPSCheckTag(1), this.mapSize.width, this.mapSize.height);
    }
    let endTime = new Date().getTime();
    cc.log(endTime - startTime);
    if (this.jpsFindPath) {
      this.graphics.clear();
      this.lightIndex = this.jpsFindPath.length - 1;
      this.interval = setInterval(this.lightOnJPSNode.bind(this), 100);
    }
  }

  initAstarTileList() {
    for (let h = 0; h < this.mapSize.width; h++) {
      for (let v = 0; v < this.mapSize.height; v++) {

        let td = new TileData();
        td.tileCenterPosition = this.tileLayerInfo.getPositionAt(cc.v2(h, v));
        let pointnode = cc.instantiate(this.pointPrefab);
        this.tileLayerInfo.node.addChild(pointnode);
        let p = td.tileCenterPosition
        pointnode.setPosition(p.x + this.tileSize.width / 2, p.y + this.tileSize.height / 2);
        let gid = this.tileLayerInfo.getTileGIDAt(cc.v2(h, v));
        let index = v * this.mapSize.width + h
        this.tileNodeList[index] = pointnode.getComponent("PointNode");
        let astarNode = new AStarNode<any, number>();
        astarNode.corde = cc.v2(h, v);
        astarNode.myIndex = index
        astarNode.myTag = new AStarCheckTag(gid);
        this.astarNodes[index] = astarNode;
        if (gid == 2) {
          this.startNode = astarNode;
        }
        if (gid == 3) {
          this.endNode = astarNode;
        }
      }
    }

  }

  initJPSTileList() {
    for (let h = 0; h < this.mapSize.width; h++) {
      for (let v = 0; v < this.mapSize.height; v++) {

        let td = new TileData();
        td.tileCenterPosition = this.tileLayerInfo.getPositionAt(cc.v2(h, v));
        let pointnode = cc.instantiate(this.pointPrefab);
        this.tileLayerInfo.node.addChild(pointnode);
        let p = td.tileCenterPosition
        pointnode.setPosition(p.x + this.tileSize.width / 2, p.y + this.tileSize.height / 2);
        let gid = this.tileLayerInfo.getTileGIDAt(cc.v2(h, v));
        let index = v * this.mapSize.width + h
        this.tileNodeList[index] = pointnode.getComponent("PointNode");
        let astarNode = new JPSNode<any, number>();
        astarNode.corde = cc.v2(h, v);
        astarNode.myIndex = index
        astarNode.myTag = new JPSCheckTag(gid);
        this.jpsNodes[index] = astarNode;
        if (gid == 2) {
          this.jpsStartNode = astarNode;
        }
        if (gid == 3) {
          this.jpsEndNode = astarNode;
        }
      }
    }

  }
  interval: number = 0;
  lightIndex: number = 0;
  lightOnAstarNode() {
    if (this.lightIndex >= 0) {
      let node = this.tileNodeList[this.finalPath[this.lightIndex].myIndex];
      // node.lightOn();
      this.lineAstarNode(this.finalPath[this.lightIndex])
      this.lightIndex--;

    } else {
      clearInterval(this.interval);
    }
  }
  lightOnJPSNode() {
    if (this.lightIndex >= 0) {
      let node = this.tileNodeList[this.jpsFindPath[this.lightIndex].myIndex];
      // node.lightOn();
      this.lineJPSNode(this.jpsFindPath[this.lightIndex])
      this.lightIndex--;

    } else {
      clearInterval(this.interval);
    }
  }
  lastJPSNode: JPSNode<any, number> = null;
  lineJPSNode(point: JPSNode<any, number>) {
    let pe = this.tileLayerInfo.getPositionAt(point.corde);
    this.graphics.moveTo(pe.x + this.tileSize.width / 2, pe.y + this.tileSize.height / 2);
    if (this.lastJPSNode != null) {
      let ps = this.tileLayerInfo.getPositionAt(this.lastJPSNode.corde)
      this.graphics.lineTo(ps.x + this.tileSize.width / 2, ps.y + this.tileSize.height / 2);
      this.graphics.stroke();
    }
    this.lastJPSNode = point;
  }
  lastAstarNode: AStarNode<any, number> = null;
  lineAstarNode(point: AStarNode<any, number>) {
    let pe = this.tileLayerInfo.getPositionAt(point.corde);
    this.graphics.moveTo(pe.x + this.tileSize.width / 2, pe.y + this.tileSize.height / 2);
    if (this.lastAstarNode != null) {
      let ps = this.tileLayerInfo.getPositionAt(this.lastAstarNode.corde)
      this.graphics.lineTo(ps.x + this.tileSize.width / 2, ps.y + this.tileSize.height / 2);
      this.graphics.stroke();
    }
    this.lastAstarNode = point;
  }

  update(dt) {
    // this.jpsFindPath = this.jpsLogic.findPath(this.jpsStartNode, this.jpsEndNode, this.jpsNodes, new JPSCheckTag(0), new JPSCheckTag(1), this.mapSize.width, this.mapSize.height);
    // this.finalPath = this.astarLogic.findPath(this.startNode, this.endNode, this.astarNodes, new AStarCheckTag(0), new AStarCheckTag(1), this.mapSize.width, this.mapSize.height);
  }

}
